#include <stdlib.h>
#include <string.h>
#include <types.h>
#include <bitops.h>
#include <device.h>
#include <printk.h>
#include "board.h"
#include "os.h"
#include <syserr.h>
#include "dev.h"
#include "settings.h"
#include <math.h>


#ifdef configUSING_VEML6030


uint16_t  veml6030_cfg = ((VALUE_CONFIGURE & (~IT_MASK))&(~ALS_GAIN_MASK))|(IT_100)|(ALS_GAIN_1_8);

struct veml6030_data
{
    struct i2c_bus_device *i2c_bus;
};

static struct veml6030_data veml6030_data;

uint16_t int_time_table[] = 
{
    IT_25,IT_50,IT_100,IT_200,IT_400,IT_800
};


uint16_t gain_table[] = 
{
    ALS_GAIN_1_8,ALS_GAIN_1_4,ALS_GAIN_1,ALS_GAIN_2
};
const uint8_t int_time_index   = 0;
const uint8_t gain_table_index = 0 ;

float resolution_table[6][4] = 
{
    {1.8432,0.9216,0.2304,0.1152},
    {0.9216,0.4608,0.1152,0.0576},
    {0.4608,0.2304,0.0576,0.0288},
    {0.2304,0.1152,0.0288,0.0144},
    {0.1152,0.0576,0.0144,0.0072},
    {0.0576,0.0288,0.0072,0.0036},
};


static int veml6030_read_reg(struct i2c_bus_device *i2c_bus, uint8_t reg, uint8_t *values, size_t length)
{
    struct i2c_msg msgs[2];
    msgs[0].addr  = VEML6030_ADDR;
    msgs[0].flags = I2C_WR;
    msgs[0].buf   = &reg;
    msgs[0].len   = 1;
    msgs[1].addr  = VEML6030_ADDR;
    msgs[1].flags = I2C_RD; /* Read from slave */
    msgs[1].buf   = (uint8_t *)values;
    msgs[1].len   = length;
    if (i2c_transfer(i2c_bus, msgs, 2) == 2)
        return 0;
    return -EIO;
}

static int veml6030_write_reg(struct i2c_bus_device *i2c_bus, uint8_t reg, uint8_t *values, size_t length)
{
    uint8_t tmp[0x20];
    struct i2c_msg msgs[1];
    
    tmp[0] = reg;
    memcpy(&tmp[1], values, length);

    msgs[0].addr  = VEML6030_ADDR;
    msgs[0].flags = I2C_WR;
    msgs[0].buf   = tmp;
    msgs[0].len   = length+1;

    if (i2c_transfer(i2c_bus, msgs, 1) == 1)
        return 0;

    return -EIO;
}




static void veml6030_shut_down(const struct veml6030_data *veml6030)
{
    uint16_t config = veml6030_cfg |(CONFIGURE_SHUT_DOWN);
    veml6030_write_reg(veml6030->i2c_bus, REG_CONFIGURE,(uint8_t *) &config, 2);
}

static void veml6030_power_on(const struct veml6030_data *veml6030)
{
    uint16_t config = veml6030_cfg &(CONFIGURE_POWER_0N);
    veml6030_write_reg(veml6030->i2c_bus, REG_CONFIGURE,(uint8_t *) &config, 2);
    board_mdelay(3);
}

void veml6030_reset(const struct veml6030_data *veml6030)
{
    uint8_t i;
    uint8_t reg[]    = { REG_CONFIGURE, REG_THR_H, REG_THR_L, REG_PSM};
    uint16_t value[] = {veml6030_cfg, VALUE_THR_H, VALUE_THR_L, VALUE_PSM};
    
    veml6030_shut_down(veml6030);
    veml6030_cfg =  ( VALUE_CONFIGURE | int_time_table[int_time_index] | gain_table[gain_table_index] );
    value[0] = veml6030_cfg;
    
    for(i = 0; i < sizeof(reg); i++)
    {
        veml6030_write_reg(veml6030->i2c_bus, reg[i],(uint8_t *) &value[i], 2);
    }
    veml6030_power_on(veml6030);
}


void veml6030_config(const struct veml6030_data *veml6030,uint16_t config)
{
   veml6030_shut_down(veml6030) ;
   veml6030_write_reg(veml6030->i2c_bus, REG_CONFIGURE,(uint8_t *) &config, 2);
   veml6030_power_on(veml6030);
}

// y(lux) = 6.0135E-13x4 - 9.3924E-09x3 + 8.1488E-05x2 + 1.0023E+00x
static uint32_t lux_calc(uint32_t lux)
{
    double ex ,ex1,ex2,ex3,ex4 = 0;
    ex1 = pow(10, (-13));
    ex1 = 6.0135 * ex1 * lux *lux*lux*lux;
    ex2 = pow(10, (-9));
    ex2 = 9.3924 *ex2 * lux *lux*lux;

    ex3 = pow(10, (-5));
    ex3 = 8.1488 *ex3 * lux *lux;
    ex4 = 1.0023 *lux;
    ex = ex1-ex2+ex3+ex4;
    //printf("--------ex:%f,%f,%f,%f\r\n",ex1,ex2,ex3,ex4);
    return (uint32_t)ex;
}

static size_t veml6030_get_sensor_value(device_t *dev, off_t pos, void *buffer, size_t size)
{
    struct veml6030_data *veml6030 = (struct veml6030_data *)dev->user_data;
	uint8_t config[2], data1[2],data2[2];
    uint16_t value = 0,value1 = 0,value2 = 0;
    
    double  lux, white;
    err_t err = veml6030_read_reg(veml6030->i2c_bus, REG_CONFIGURE, config, 2);
	 
    if (err < 0) 
			return (0);
    value = (config[1] << 8) | config[0];
    //log_d( "veml6030  config =0x%04X!\n", value);
    if(veml6030_cfg != value)  //error, need reset!
    {
		log_d( "veml6030 reset!\n");
        veml6030_reset(veml6030);
        return 0;
    }
    err = veml6030_read_reg(veml6030->i2c_bus, REG_ALSHR_DT, data1, 2);
    if (err < 0) return (0);
    err = veml6030_read_reg(veml6030->i2c_bus, REG_WHITE_DT, data2, 2);
    if (err < 0) return (0);
    value1 = data1[1]<< 8 | data1[0];
    value2 = get_le_val(data2, 2);
    

    lux = (float)value1 * resolution_table[int_time_index][gain_table_index];//???
    if(lux > 10000)
    {
        lux = lux_calc(lux);
    }
	//lux
    return lux;
}

static const struct device_ops veml6030_dev_ops =
{
    .read = veml6030_get_sensor_value,
};

static struct device veml6030_dev;

err_t veml6030_init(const char *i2c_bus_name)
{
    struct i2c_bus_device *i2c_bus = (struct i2c_bus_device *)device_find(i2c_bus_name);
    if (i2c_bus == NULL)
    {
        log_d( "\ni2c_bus %s for veml6030 not found!\n", i2c_bus_name);
        return -ENOSYS;
    }

    if (device_open(&i2c_bus->parent, NULL) != 0)
    {
       log_d("\ni2c_bus %s for veml6030 opened failed!\n", i2c_bus_name);
        return -EIO;
    }
    veml6030_data.i2c_bus  = i2c_bus;
    veml6030_dev.ops = &veml6030_dev_ops;
    veml6030_dev.user_data = &veml6030_data;

    veml6030_reset(&veml6030_data);

    device_register(&veml6030_dev, "veml6030", 0);
    return 0;
}

#endif
